package com.starsea.book.ui.newpage.weidget.banner

import android.app.Activity
import android.content.Context
import android.content.res.TypedArray
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.os.Build
import android.text.TextUtils
import android.util.AttributeSet
import android.util.TypedValue
import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ImageView.ScaleType
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.annotation.DimenRes
import androidx.annotation.DrawableRes
import androidx.annotation.LayoutRes
import androidx.core.view.ViewCompat
import androidx.viewpager.widget.PagerAdapter
import androidx.viewpager.widget.ViewPager.OnPageChangeListener
import androidx.viewpager.widget.ViewPager.PageTransformer
import com.starsea.book.R
import com.starsea.book.ui.newpage.weidget.banner.BannerViewPager.AutoPlayDelegate
import com.starsea.book.ui.newpage.weidget.banner.transformer.BannerPageTransformer
import com.starsea.book.ui.newpage.weidget.banner.transformer.TransitionEffect
import java.lang.ref.WeakReference


class Banner @JvmOverloads constructor(
  context: Context,
  attrs: AttributeSet,
  defStyle: Int = 0
) : RelativeLayout(context, attrs, defStyle), AutoPlayDelegate, OnPageChangeListener {
  var viewPager: BannerViewPager? = null
    private set
  private var mHackyViews: MutableList<View?>? = null
  private var mViews: MutableList<View?>? = null
  var tips: List<String?>? = null
    private set
  private var mPointRealContainerLl: LinearLayout? = null
  private var mTipTv: TextView? = null
  private var mAutoPlayAble = true
  private var mAutoPlayInterval = 3000
  private var mPageChangeDuration = 800
  private var mPointGravity = Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM
  private var mPointLeftRightMargin = 0
  private var mPointTopBottomMargin = 0
  private var mPointContainerLeftRightPadding = 0
  private var mTipTextSize = 0
  private var mTipTextColor = Color.WHITE
  private var mPointDrawableResId = R.drawable.banner_selector_point_solid
  private var mPointContainerBackgroundDrawable: Drawable? = null
  private var mAutoPlayTask: AutoPlayTask? = null
  private var mPageScrollPosition = 0
  private var mPageScrollPositionOffset = 0f
  private var mTransitionEffect: TransitionEffect? = null
  private var mPlaceholderIv: ImageView? = null
  private var mScaleType = ScaleType.CENTER_CROP
  private var mPlaceholderDrawableResId = NO_PLACEHOLDER_DRAWABLE
  private var mModels: List<Any?>? = null
  private var mDelegate: Delegate? = null
  private var mAdapter: Adapter? = null
  private var mOverScrollMode = OVER_SCROLL_NEVER
  private var mOnPageChangeListener: OnPageChangeListener? = null
  private var mPointContainerRl: RelativeLayout? = null
  private var mIsNumberIndicator = false
  private var mNumberIndicatorTv: TextView? = null
  private var mNumberIndicatorTextColor = Color.WHITE
  private var mNumberIndicatorTextSize = 0
  private var mNumberIndicatorBackground: Drawable? = null
  private var mIsNeedShowIndicatorOnOnlyOnePage = false
  private var mContentBottomMargin = 0
  private var mAspectRatio = 0f
  private var mAllowUserScrollable = true
  private var mSkipView: View? = null
  private var mEnterView: View? = null
  private var mGuideDelegate: GuideDelegate? = null
  private var mIsFirstInvisible = true
  private val mGuideOnNoDoubleClickListener: OnNoDoubleClickListener =
    object : OnNoDoubleClickListener() {
      override fun onNoDoubleClick(v: View?) {
        if (mGuideDelegate != null) {
          mGuideDelegate!!.onClickEnterOrSkip()
        }
      }
    }

  init {
    initDefaultAttrs(context)
    initCustomAttrs(context, attrs)
    initView(context)
  }

  private fun initDefaultAttrs(context: Context) {
    mAutoPlayTask = AutoPlayTask(this)
    mPointLeftRightMargin = BannerUtil.dp2px(context, 3f)
    mPointTopBottomMargin = BannerUtil.dp2px(context, 6f)
    mPointContainerLeftRightPadding = BannerUtil.dp2px(context, 10f)
    mTipTextSize = BannerUtil.sp2px(context, 10f)
    mPointContainerBackgroundDrawable = ColorDrawable(Color.parseColor("#44aaaaaa"))
    mTransitionEffect = TransitionEffect.Default
    mNumberIndicatorTextSize = BannerUtil.sp2px(context, 10f)
    mContentBottomMargin = 0
    mAspectRatio = 0f
  }

  private fun initCustomAttrs(context: Context, attrs: AttributeSet) {
    val typedArray = context.obtainStyledAttributes(attrs, R.styleable.Banner)
    val N = typedArray.indexCount
    for (i in 0 until N) {
      initCustomAttr(typedArray.getIndex(i), typedArray)
    }
    typedArray.recycle()
  }

  private fun initCustomAttr(attr: Int, typedArray: TypedArray) {
    if (attr == R.styleable.Banner_banner_pointDrawable) {
      mPointDrawableResId =
        typedArray.getResourceId(attr, R.drawable.banner_selector_point_solid)
    } else if (attr == R.styleable.Banner_banner_pointContainerBackground) {
      mPointContainerBackgroundDrawable = typedArray.getDrawable(attr)
    } else if (attr == R.styleable.Banner_banner_pointLeftRightMargin) {
      mPointLeftRightMargin = typedArray.getDimensionPixelSize(attr, mPointLeftRightMargin)
    } else if (attr == R.styleable.Banner_banner_pointContainerLeftRightPadding) {
      mPointContainerLeftRightPadding =
        typedArray.getDimensionPixelSize(attr, mPointContainerLeftRightPadding)
    } else if (attr == R.styleable.Banner_banner_pointTopBottomMargin) {
      mPointTopBottomMargin = typedArray.getDimensionPixelSize(attr, mPointTopBottomMargin)
    } else if (attr == R.styleable.Banner_banner_indicatorGravity) {
      mPointGravity = typedArray.getInt(attr, mPointGravity)
    } else if (attr == R.styleable.Banner_banner_pointAutoPlayAble) {
      mAutoPlayAble = typedArray.getBoolean(attr, mAutoPlayAble)
    } else if (attr == R.styleable.Banner_banner_pointAutoPlayInterval) {
      mAutoPlayInterval = typedArray.getInteger(attr, mAutoPlayInterval)
    } else if (attr == R.styleable.Banner_banner_pageChangeDuration) {
      mPageChangeDuration = typedArray.getInteger(attr, mPageChangeDuration)
    } else if (attr == R.styleable.Banner_banner_transitionEffect) {
      val ordinal = typedArray.getInt(attr, TransitionEffect.Accordion.ordinal)
      mTransitionEffect = TransitionEffect.values()[ordinal]
    } else if (attr == R.styleable.Banner_banner_tipTextColor) {
      mTipTextColor = typedArray.getColor(attr, mTipTextColor)
    } else if (attr == R.styleable.Banner_banner_tipTextSize) {
      mTipTextSize = typedArray.getDimensionPixelSize(attr, mTipTextSize)
    } else if (attr == R.styleable.Banner_banner_placeholderDrawable) {
      mPlaceholderDrawableResId = typedArray.getResourceId(attr, mPlaceholderDrawableResId)
    } else if (attr == R.styleable.Banner_banner_isNumberIndicator) {
      mIsNumberIndicator = typedArray.getBoolean(attr, mIsNumberIndicator)
    } else if (attr == R.styleable.Banner_banner_numberIndicatorTextColor) {
      mNumberIndicatorTextColor = typedArray.getColor(attr, mNumberIndicatorTextColor)
    } else if (attr == R.styleable.Banner_banner_numberIndicatorTextSize) {
      mNumberIndicatorTextSize = typedArray.getDimensionPixelSize(attr, mNumberIndicatorTextSize)
    } else if (attr == R.styleable.Banner_banner_numberIndicatorBackground) {
      mNumberIndicatorBackground = typedArray.getDrawable(attr)
    } else if (attr == R.styleable.Banner_banner_isNeedShowIndicatorOnOnlyOnePage) {
      mIsNeedShowIndicatorOnOnlyOnePage =
        typedArray.getBoolean(attr, mIsNeedShowIndicatorOnOnlyOnePage)
    } else if (attr == R.styleable.Banner_banner_contentBottomMargin) {
      mContentBottomMargin = typedArray.getDimensionPixelSize(attr, mContentBottomMargin)
    } else if (attr == R.styleable.Banner_banner_aspectRatio) {
      mAspectRatio = typedArray.getFloat(attr, mAspectRatio)
    } else if (attr == R.styleable.Banner_android_scaleType) {
      val index = typedArray.getInt(attr, -1)
      if (index >= 0 && index < sScaleTypeArray.size) {
        mScaleType = sScaleTypeArray[index]
      }
    }
  }

  private fun initView(context: Context) {
    mPointContainerRl = RelativeLayout(context)
    if (Build.VERSION.SDK_INT >= 16) {
      mPointContainerRl!!.background = mPointContainerBackgroundDrawable
    } else {
      mPointContainerRl!!.setBackgroundDrawable(mPointContainerBackgroundDrawable)
    }
    mPointContainerRl!!.setPadding(
      mPointContainerLeftRightPadding,
      mPointTopBottomMargin,
      mPointContainerLeftRightPadding,
      mPointTopBottomMargin
    )
    val pointContainerLp = LayoutParams(RMP, RWC)
    // 处理圆点在顶部还是底部
    if (mPointGravity and Gravity.VERTICAL_GRAVITY_MASK == Gravity.TOP) {
      pointContainerLp.addRule(ALIGN_PARENT_TOP)
    } else {
      pointContainerLp.addRule(ALIGN_PARENT_BOTTOM)
    }
    addView(mPointContainerRl, pointContainerLp)
    val indicatorLp = LayoutParams(RWC, RWC)
    indicatorLp.addRule(CENTER_VERTICAL)
    if (mIsNumberIndicator) {
      mNumberIndicatorTv = TextView(context)
      mNumberIndicatorTv!!.id = R.id.banner_indicatorId
      mNumberIndicatorTv!!.gravity = Gravity.CENTER
      mNumberIndicatorTv!!.isSingleLine = true
      mNumberIndicatorTv!!.ellipsize = TextUtils.TruncateAt.END
      mNumberIndicatorTv!!.setTextColor(mNumberIndicatorTextColor)
      mNumberIndicatorTv!!.setTextSize(
        TypedValue.COMPLEX_UNIT_PX,
        mNumberIndicatorTextSize.toFloat()
      )
      mNumberIndicatorTv!!.visibility = INVISIBLE
      if (mNumberIndicatorBackground != null) {
        if (Build.VERSION.SDK_INT >= 16) {
          mNumberIndicatorTv!!.background = mNumberIndicatorBackground
        } else {
          mNumberIndicatorTv!!.setBackgroundDrawable(mNumberIndicatorBackground)
        }
      }
      mPointContainerRl!!.addView(mNumberIndicatorTv, indicatorLp)
    } else {
      mPointRealContainerLl = LinearLayout(context)
      mPointRealContainerLl!!.id = R.id.banner_indicatorId
      mPointRealContainerLl!!.orientation = LinearLayout.HORIZONTAL
      mPointRealContainerLl!!.gravity = Gravity.CENTER_VERTICAL
      mPointContainerRl!!.addView(mPointRealContainerLl, indicatorLp)
    }
    val tipLp = LayoutParams(RMP, RWC)
    tipLp.addRule(CENTER_VERTICAL)
    mTipTv = TextView(context)
    mTipTv!!.gravity = Gravity.CENTER_VERTICAL
    mTipTv!!.isSingleLine = true
    mTipTv!!.ellipsize = TextUtils.TruncateAt.END
    mTipTv!!.setTextColor(mTipTextColor)
    mTipTv!!.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTipTextSize.toFloat())
    mPointContainerRl!!.addView(mTipTv, tipLp)
    val horizontalGravity = mPointGravity and Gravity.HORIZONTAL_GRAVITY_MASK
    // 处理圆点在左边、右边还是水平居中
    if (horizontalGravity == Gravity.LEFT) {
      indicatorLp.addRule(ALIGN_PARENT_LEFT)
      tipLp.addRule(RIGHT_OF, R.id.banner_indicatorId)
      mTipTv!!.gravity = Gravity.CENTER_VERTICAL or Gravity.RIGHT
    } else if (horizontalGravity == Gravity.RIGHT) {
      indicatorLp.addRule(ALIGN_PARENT_RIGHT)
      tipLp.addRule(LEFT_OF, R.id.banner_indicatorId)
    } else {
      indicatorLp.addRule(CENTER_HORIZONTAL)
      tipLp.addRule(LEFT_OF, R.id.banner_indicatorId)
    }
    showPlaceholder()
  }

  fun showPlaceholder() {
    if (mPlaceholderIv == null && mPlaceholderDrawableResId != NO_PLACEHOLDER_DRAWABLE) {
      mPlaceholderIv = BannerUtil.getItemImageView(
        context,
        mPlaceholderDrawableResId,
        LocalImageSize(720, 360, 640f, 320f),
        mScaleType
      )
      val layoutParams = LayoutParams(RMP, RMP)
      layoutParams.setMargins(0, 0, 0, mContentBottomMargin)
      addView(mPlaceholderIv, layoutParams)
    }
  }

  /**
   * 设置页码切换过程的时间长度
   *
   * @param duration 页码切换过程的时间长度
   */
  fun setPageChangeDuration(duration: Int) {
    if (duration >= 0 && duration <= 2000) {
      mPageChangeDuration = duration
      if (viewPager != null) {
        viewPager!!.setPageChangeDuration(duration)
      }
    }
  }

  /**
   * 设置是否开启自动轮播，需要在 setData 方法之前调用，并且调了该方法后必须再调用一次 setData 方法
   * 例如根据图片当图片数量大于 1 时开启自动轮播，等于 1 时不开启自动轮播
   */
  fun setAutoPlayAble(autoPlayAble: Boolean) {
    mAutoPlayAble = autoPlayAble
    stopAutoPlay()
    if (viewPager != null && viewPager!!.adapter != null) {
      viewPager!!.adapter!!.notifyDataSetChanged()
    }
  }

  /**
   * 设置自动轮播的时间间隔
   */
  fun setAutoPlayInterval(autoPlayInterval: Int) {
    mAutoPlayInterval = autoPlayInterval
  }

  /**
   * 设置每一页的控件、数据模型和文案
   *
   * @param views  每一页的控件集合
   * @param models 每一页的数据模型集合
   * @param tips   每一页的提示文案集合
   */
  fun setData(views: MutableList<View?>, models: List<Any?>?, tips: List<String?>?) {
    var views = views
    var models = models
    var tips = tips
    if (BannerUtil.isCollectionEmpty(views)) {
      mAutoPlayAble = false
      views = ArrayList()
      models = ArrayList()
      tips = ArrayList()
    }
    if (mAutoPlayAble && views.size < 3 && mHackyViews == null) {
      mAutoPlayAble = false
    }
    mModels = models
    mViews = views
    this.tips = tips
    initIndicator()
    initViewPager()
    removePlaceholder()
    handleGuideViewVisibility(0, 0f)
  }

  /**
   * 设置布局资源id、数据模型和文案
   *
   * @param layoutResId item布局文件资源id
   * @param models      每一页的数据模型集合
   * @param tips        每一页的提示文案集合
   */
  fun setData(@LayoutRes layoutResId: Int, models: List<Any?>?, tips: List<String?>?) {
    var models = models
    var tips = tips
    mViews = ArrayList()
    if (models == null) {
      models = ArrayList()
      tips = ArrayList()
    }
    for (i in models.indices) {
      mViews!!.add(inflateItemView(layoutResId))
    }
    if (mAutoPlayAble && mViews!!.size < 3) {
      mHackyViews = ArrayList(mViews!!)
      mHackyViews!!.add(inflateItemView(layoutResId))
      if (mHackyViews!!.size == 2) {
        mHackyViews!!.add(inflateItemView(layoutResId))
      }
    }
    setData(mViews!!, models, tips)
  }

  private fun inflateItemView(@LayoutRes layoutResId: Int): View {
    val itemView = inflate(context, layoutResId, null)
    if (itemView is ImageView) {
      itemView.scaleType = mScaleType
    }
    return itemView
  }

  /**
   * 设置数据模型和文案，布局资源默认为 ImageView
   *
   * @param models 每一页的数据模型集合
   * @param tips   每一页的提示文案集合
   */
  fun setData(models: List<Any?>?, tips: List<String?>?) {
    setData(R.layout.banner_item_image, models, tips)
  }

  /**
   * 设置每一页的控件集合，主要针对引导页的情况
   *
   * @param views 每一页的控件集合
   */
  fun setData(views: MutableList<View?>) {
    setData(views, null, null)
  }

  /**
   * 设置每一页图片的资源 id，主要针对引导页的情况
   *
   * @param localImageSize 内存优化，Bitmap 的宽高在 maxWidth maxHeight 和 minWidth minHeight 之间，传 null 的话默认为 720, 1280, 320, 640
   * @param scaleType      图片缩放模式，传 null 的话默认为 CENTER_CROP
   * @param resIds         每一页图片资源 id
   */
  fun setData(
    localImageSize: LocalImageSize?,
    scaleType: ScaleType?,
    @DrawableRes vararg resIds: Int
  ) {
    var localImageSize = localImageSize
    if (localImageSize == null) {
      localImageSize = LocalImageSize(720, 1280, 320f, 640f)
    }
    if (scaleType != null) {
      mScaleType = scaleType
    }
    val views: MutableList<View?> = ArrayList()
    for (resId in resIds) {
      views.add(BannerUtil.getItemImageView(context, resId, localImageSize, mScaleType))
    }
    setData(views)
  }

  /**
   * 设置是否允许用户手指滑动
   *
   * @param allowUserScrollable true表示允许跟随用户触摸滑动，false反之
   */
  fun setAllowUserScrollable(allowUserScrollable: Boolean) {
    mAllowUserScrollable = allowUserScrollable
    if (viewPager != null) {
      viewPager!!.setAllowUserScrollable(mAllowUserScrollable)
    }
  }

  /**
   * 添加ViewPager滚动监听器
   */
  fun setOnPageChangeListener(onPageChangeListener: OnPageChangeListener?) {
    mOnPageChangeListener = onPageChangeListener
  }

  /**
   * 设置进入按钮和跳过按钮控件资源 id，需要开发者自己处理这两个按钮的点击事件
   *
   * @param enterResId 进入按钮控件
   * @param skipResId  跳过按钮控件
   */
  fun setEnterSkipViewId(enterResId: Int, skipResId: Int) {
    if (enterResId != 0) {
      mEnterView = (context as Activity).findViewById(enterResId)
    }
    if (skipResId != 0) {
      mSkipView = (context as Activity).findViewById(skipResId)
    }
  }

  /**
   * 设置进入按钮和跳过按钮控件资源 id 及其点击事件监听器
   * 如果进入按钮和跳过按钮有一个不存在的话就传 0
   *
   * @param enterResId    进入按钮控件资源 id，没有的话就传 0
   * @param skipResId     跳过按钮控件资源 id，没有的话就传 0
   * @param guideDelegate 引导页「进入」和「跳过」按钮点击事件监听器
   */
  fun setEnterSkipViewIdAndDelegate(
    enterResId: Int,
    skipResId: Int,
    guideDelegate: GuideDelegate?
  ) {
    if (guideDelegate != null) {
      mGuideDelegate = guideDelegate
      if (enterResId != 0) {
        mEnterView = (context as Activity).findViewById(enterResId)
        mEnterView!!.setOnClickListener(mGuideOnNoDoubleClickListener)
      }
      if (skipResId != 0) {
        mSkipView = (context as Activity).findViewById(skipResId)
        mSkipView!!.setOnClickListener(mGuideOnNoDoubleClickListener)
      }
    }
    handleGuideViewVisibility(0, 0f)
  }

  var currentItem: Int
    /**
     * 获取当前选中界面索引
     */
    get() = if (viewPager == null || BannerUtil.isCollectionEmpty(mViews)) {
      -1
    } else {
      viewPager!!.currentItem % mViews!!.size
    }
    set(item) {
      if (viewPager == null || mViews == null || item > itemCount - 1) {
        return
      }
      if (mAutoPlayAble) {
        val realCurrentItem = viewPager!!.currentItem
        val currentItem = realCurrentItem % mViews!!.size
        val offset = item - currentItem

        // 这里要使用循环递增或递减设置，否则会ANR
        if (offset < 0) {
          for (i in -1 downTo offset) {
            viewPager!!.setCurrentItem(realCurrentItem + i, false)
          }
        } else if (offset > 0) {
          for (i in 1..offset) {
            viewPager!!.setCurrentItem(realCurrentItem + i, false)
          }
        }
        startAutoPlay()
      } else {
        viewPager!!.setCurrentItem(item, false)
      }
    }
  val itemCount: Int
    /**
     * 获取广告页面总个数
     */
    get() = if (mViews == null) 0 else mViews!!.size
  val views: MutableList<View?>?
    get() = mViews

  fun <VT : View?> getItemView(position: Int): VT? {
    return if (mViews == null) null else mViews!![position] as VT
  }

  fun getItemImageView(position: Int): ImageView {
    return getItemView<ImageView>(position)!!
  }

  override fun setOverScrollMode(overScrollMode: Int) {
    mOverScrollMode = overScrollMode
    if (viewPager != null) {
      viewPager!!.overScrollMode = mOverScrollMode
    }
  }

  fun setIndicatorVisibility(visible: Boolean) {
    mPointContainerRl!!.visibility =
      if (visible) VISIBLE else GONE
  }

  fun setIndicatorTopBottomMarginDp(marginDp: Int) {
    setIndicatorTopBottomMarginPx(BannerUtil.dp2px(context, marginDp.toFloat()))
  }

  fun setIndicatorTopBottomMarginRes(@DimenRes resId: Int) {
    setIndicatorTopBottomMarginPx(resources.getDimensionPixelOffset(resId))
  }

  fun setIndicatorTopBottomMarginPx(marginPx: Int) {
    mPointTopBottomMargin = marginPx
    mPointContainerRl!!.setPadding(
      mPointContainerLeftRightPadding,
      mPointTopBottomMargin,
      mPointContainerLeftRightPadding,
      mPointTopBottomMargin
    )
  }

  private fun initIndicator() {
    if (mPointRealContainerLl != null) {
      mPointRealContainerLl!!.removeAllViews()
      if (mIsNeedShowIndicatorOnOnlyOnePage || !mIsNeedShowIndicatorOnOnlyOnePage && mViews!!.size > 1) {
        val lp = LinearLayout.LayoutParams(LWC, LWC)
        lp.setMargins(mPointLeftRightMargin, 0, mPointLeftRightMargin, 0)
        var imageView: ImageView
        for (i in mViews!!.indices) {
          imageView = ImageView(context)
          imageView.layoutParams = lp
          imageView.setImageResource(mPointDrawableResId)
          mPointRealContainerLl!!.addView(imageView)
        }
      }
    }
    if (mNumberIndicatorTv != null) {
      if (mIsNeedShowIndicatorOnOnlyOnePage || !mIsNeedShowIndicatorOnOnlyOnePage && mViews!!.size > 1) {
        mNumberIndicatorTv!!.visibility = VISIBLE
      } else {
        mNumberIndicatorTv!!.visibility = INVISIBLE
      }
    }
  }

  private fun initViewPager() {
    if (viewPager != null && this == viewPager!!.parent) {
      removeView(viewPager)
      viewPager = null
    }
    viewPager = BannerViewPager(context)
    viewPager!!.offscreenPageLimit = 1
    viewPager!!.adapter = PageAdapter()
    viewPager!!.addOnPageChangeListener(this)
    viewPager!!.overScrollMode = mOverScrollMode
    viewPager!!.setAllowUserScrollable(mAllowUserScrollable)
    viewPager!!.setPageTransformer(
      true,
      BannerPageTransformer.Companion.getPageTransformer(mTransitionEffect)
    )
    setPageChangeDuration(mPageChangeDuration)
    val layoutParams = LayoutParams(RMP, RMP)
    layoutParams.setMargins(0, 0, 0, mContentBottomMargin)
    addView(viewPager, 0, layoutParams)
    if (mAutoPlayAble && !BannerUtil.isCollectionEmpty(mViews)) {
      viewPager!!.setAutoPlayDelegate(this)
      val zeroItem = Int.MAX_VALUE / 2 - Int.MAX_VALUE / 2 % mViews!!.size
      viewPager!!.currentItem = zeroItem
      startAutoPlay()
    } else {
      switchToPoint(0)
    }
  }

  fun removePlaceholder() {
    if (mPlaceholderIv != null && this == mPlaceholderIv!!.parent) {
      removeView(mPlaceholderIv)
      mPlaceholderIv = null
    }
  }

  /**
   * 设置宽高比例，如果大于 0，则会根据宽度来计算高度，否则使用 android:layout_height 指定的高度
   */
  fun setAspectRatio(aspectRatio: Float) {
    mAspectRatio = aspectRatio
    requestLayout()
  }

  override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    var heightMeasureSpec = heightMeasureSpec
    if (mAspectRatio > 0) {
      val width = MeasureSpec.getSize(widthMeasureSpec)
      val height = (width / mAspectRatio).toInt()
      heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
    }
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
  }

  override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
    if (mAutoPlayAble) {
      when (ev.action) {
        MotionEvent.ACTION_DOWN -> stopAutoPlay()
        MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> startAutoPlay()
      }
    }
    return super.dispatchTouchEvent(ev)
  }

  /**
   * 设置当只有一页数据时是否显示指示器
   */
  fun setIsNeedShowIndicatorOnOnlyOnePage(isNeedShowIndicatorOnOnlyOnePage: Boolean) {
    mIsNeedShowIndicatorOnOnlyOnePage = isNeedShowIndicatorOnOnlyOnePage
  }

  override fun onVisibilityChanged(changedView: View, visibility: Int) {
    super.onVisibilityChanged(changedView, visibility)
    if (visibility == VISIBLE) {
      startAutoPlay()
    } else if (visibility == INVISIBLE || visibility == GONE) {
      onInvisibleToUser()
    }
  }

  private fun onInvisibleToUser() {
    stopAutoPlay()

    // 处理 RecyclerView 中从对用户不可见变为可见时卡顿的问题
    if (!mIsFirstInvisible && mAutoPlayAble && viewPager != null && itemCount > 0 && mPageScrollPositionOffset != 0f) {
      viewPager!!.currentItem = viewPager!!.currentItem - 1
      viewPager!!.currentItem = viewPager!!.currentItem + 1
    }
    mIsFirstInvisible = false
  }

  override fun onDetachedFromWindow() {
    super.onDetachedFromWindow()
    onInvisibleToUser()
  }

  override fun onAttachedToWindow() {
    super.onAttachedToWindow()
    startAutoPlay()
  }

  fun startAutoPlay() {
    stopAutoPlay()
    if (mAutoPlayAble) {
      postDelayed(mAutoPlayTask, mAutoPlayInterval.toLong())
    }
  }

  fun stopAutoPlay() {
    if (mAutoPlayTask != null) {
      removeCallbacks(mAutoPlayTask)
    }
  }

  private fun switchToPoint(newCurrentPoint: Int) {
    if (mTipTv != null) {
      if (tips == null || tips!!.size < 1 || newCurrentPoint >= tips!!.size) {
        mTipTv!!.visibility = GONE
      } else {
        mTipTv!!.visibility = VISIBLE
        mTipTv!!.text = tips!![newCurrentPoint]
      }
    }
    if (mPointRealContainerLl != null) {
      if (mViews != null && mViews!!.size > 0 && newCurrentPoint < mViews!!.size && (mIsNeedShowIndicatorOnOnlyOnePage || !mIsNeedShowIndicatorOnOnlyOnePage && mViews!!.size > 1)) {
        mPointRealContainerLl!!.visibility = VISIBLE
        for (i in 0 until mPointRealContainerLl!!.childCount) {
          mPointRealContainerLl!!.getChildAt(i).isSelected = i == newCurrentPoint
          // 处理指示器选中和未选中状态图片尺寸不相等
          mPointRealContainerLl!!.getChildAt(i).requestLayout()
        }
      } else {
        mPointRealContainerLl!!.visibility = GONE
      }
    }
    if (mNumberIndicatorTv != null) {
      if (mViews != null && mViews!!.size > 0 && newCurrentPoint < mViews!!.size && (mIsNeedShowIndicatorOnOnlyOnePage || !mIsNeedShowIndicatorOnOnlyOnePage && mViews!!.size > 1)) {
        mNumberIndicatorTv!!.visibility = VISIBLE
        mNumberIndicatorTv!!.text = (newCurrentPoint + 1).toString() + "/" + mViews!!.size
      } else {
        mNumberIndicatorTv!!.visibility = GONE
      }
    }
  }

  /**
   * 设置页面切换换动画
   */
  fun setTransitionEffect(effect: TransitionEffect?) {
    mTransitionEffect = effect
    if (viewPager != null) {
      initViewPager()
      if (mHackyViews == null) {
        BannerUtil.resetPageTransformer(mViews!!)
      } else {
        BannerUtil.resetPageTransformer(mHackyViews!!)
      }
    }
  }

  /**
   * 设置自定义页面切换动画
   */
  fun setPageTransformer(transformer: PageTransformer?) {
    if (transformer != null && viewPager != null) {
      viewPager!!.setPageTransformer(true, transformer)
    }
  }

  /**
   * 切换到下一页
   */
  private fun switchToNextPage() {
    if (viewPager != null) {
      viewPager!!.currentItem = viewPager!!.currentItem + 1
    }
  }

  override fun handleAutoPlayActionUpOrCancel(xVelocity: Float) {
    if (viewPager != null) {
      if (mPageScrollPosition < viewPager!!.currentItem) { // 往右滑
        if (xVelocity > VEL_THRESHOLD || mPageScrollPositionOffset < 0.7f && xVelocity > -VEL_THRESHOLD) {
          // 已达到右滑到接下来展示左边一页的条件，展示左边一页
          viewPager!!.setBannerCurrentItemInternal(mPageScrollPosition, true)
        } else {
          // 未达到右滑到接下来展示左边一页的条件，展示当前页
          viewPager!!.setBannerCurrentItemInternal(mPageScrollPosition + 1, true)
        }
      } else if (mPageScrollPosition == viewPager!!.currentItem) { // 往左滑
        if (xVelocity < -VEL_THRESHOLD || mPageScrollPositionOffset > 0.3f && xVelocity < VEL_THRESHOLD) {
          // 已达到左滑到接下来展示右边一页的条件，展示右边一页
          viewPager!!.setBannerCurrentItemInternal(mPageScrollPosition + 1, true)
        } else {
          // 未达到左滑到接下来展示右边一页的条件，展示当前页
          viewPager!!.setBannerCurrentItemInternal(mPageScrollPosition, true)
        }
      } else {
        // 快速左滑优化异常场景。感谢 https://blog.csdn.net/lylddingHFFW/article/details/89212664
        viewPager!!.setBannerCurrentItemInternal(mPageScrollPosition, true)
      }
    }
  }

  override fun onPageSelected(position: Int) {
    var position = position
    if (BannerUtil.isCollectionEmpty(mViews)) {
      return
    }
    position = position % mViews!!.size
    switchToPoint(position)
    if (mOnPageChangeListener != null) {
      mOnPageChangeListener!!.onPageSelected(position)
    }
  }

  override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
    if (BannerUtil.isCollectionEmpty(mViews)) {
      return
    }
    handleGuideViewVisibility(position % mViews!!.size, positionOffset)
    mPageScrollPosition = position
    mPageScrollPositionOffset = positionOffset
    if (mTipTv != null) {
      if (BannerUtil.isCollectionNotEmpty(tips)) {
        mTipTv!!.visibility = VISIBLE
        val leftPosition = position % tips!!.size
        val rightPosition = (position + 1) % tips!!.size
        if (rightPosition < tips!!.size && leftPosition < tips!!.size) {
          if (positionOffset > 0.5) {
            mTipTv!!.text = tips!![rightPosition]
            ViewCompat.setAlpha(mTipTv, positionOffset)
          } else {
            ViewCompat.setAlpha(mTipTv, 1 - positionOffset)
            mTipTv!!.text = tips!![leftPosition]
          }
        }
      } else {
        mTipTv!!.visibility = GONE
      }
    }
    if (mOnPageChangeListener != null) {
      mOnPageChangeListener!!.onPageScrolled(
        position % mViews!!.size,
        positionOffset,
        positionOffsetPixels
      )
    }
  }

  override fun onPageScrollStateChanged(state: Int) {
    if (mOnPageChangeListener != null) {
      mOnPageChangeListener!!.onPageScrollStateChanged(state)
    }
  }

  private fun handleGuideViewVisibility(position: Int, positionOffset: Float) {
    if (mEnterView == null && mSkipView == null) {
      return
    }
    if (itemCount < 2) { // 只有一页时，有进入按钮则显示进入按钮并隐藏跳过按钮，没有进入按钮则显示跳过按钮
      if (mEnterView != null) {
        mEnterView!!.visibility = VISIBLE
        if (mSkipView != null) {
          mSkipView!!.visibility = GONE
        }
        return
      }
      if (mSkipView != null) {
        mSkipView!!.visibility = VISIBLE
        return
      }
    }
    if (position == itemCount - 2) {
      if (mEnterView != null) {
        ViewCompat.setAlpha(mEnterView, positionOffset)
      }
      if (mSkipView != null) {
        ViewCompat.setAlpha(mSkipView, 1.0f - positionOffset)
      }
      if (positionOffset > 0.5f) {
        if (mEnterView != null) {
          mEnterView!!.visibility = VISIBLE
        }
        if (mSkipView != null) {
          mSkipView!!.visibility = GONE
        }
      } else {
        if (mEnterView != null) {
          mEnterView!!.visibility = GONE
        }
        if (mSkipView != null) {
          mSkipView!!.visibility = VISIBLE
        }
      }
    } else if (position == itemCount - 1) {
      if (mEnterView != null) {
        ViewCompat.setAlpha(mEnterView, 1.0f - positionOffset)
      }
      if (mSkipView != null) {
        ViewCompat.setAlpha(mSkipView, positionOffset)
      }
      if (positionOffset < 0.5f) {
        if (mEnterView != null) {
          mEnterView!!.visibility = VISIBLE
        }
        if (mSkipView != null) {
          mSkipView!!.visibility = GONE
        }
      } else {
        if (mEnterView != null) {
          mEnterView!!.visibility = GONE
        }
        if (mSkipView != null) {
          mSkipView!!.visibility = VISIBLE
        }
      }
    } else {
      if (mSkipView != null) {
        mSkipView!!.visibility = VISIBLE
        ViewCompat.setAlpha(mSkipView, 1.0f)
      }
      if (mEnterView != null) {
        mEnterView!!.visibility = GONE
      }
    }
  }

  fun setDelegate(delegate: Delegate) {
    mDelegate = delegate
  }

  fun setAdapter(adapter: Adapter) {
    mAdapter = adapter
  }

  private inner class PageAdapter : PagerAdapter() {
    override fun getCount(): Int {
      return if (mViews == null) 0 else if (mAutoPlayAble) Int.MAX_VALUE else mViews!!.size
    }

    override fun instantiateItem(container: ViewGroup, position: Int): View {

//      if (BGABannerUtil.isCollectionEmpty(mViews)) {
//        return null
//      }
      val finalPosition = position % mViews!!.size
      val view: View
      view = if (mHackyViews == null) {
        mViews!![finalPosition]!!
      } else {
        mHackyViews!![position % mHackyViews!!.size]!!
      }
      if (mDelegate != null) {
        view.setOnClickListener(object : OnNoDoubleClickListener() {
          override fun onNoDoubleClick(view: View?) {
            val currentPosition = viewPager!!.currentItem % mViews!!.size
            if (BannerUtil.isIndexNotOutOfBounds(currentPosition, mModels)) {
              mDelegate!!.onBannerItemClick(
                this@Banner,
                view!!,
                mModels!![currentPosition],
                currentPosition
              )
            } else if (BannerUtil.isCollectionEmpty(mModels)) {
              mDelegate!!.onBannerItemClick(this@Banner, view!!, null, currentPosition)
            }
          }
        })
      }
      if (mAdapter != null) {
        if (BannerUtil.isIndexNotOutOfBounds(finalPosition, mModels)) {
          mAdapter!!.fillBannerItem(
            this@Banner, view,
            mModels!![finalPosition], finalPosition
          )
        } else if (BannerUtil.isCollectionEmpty(mModels)) {
          mAdapter!!.fillBannerItem(this@Banner, view, null, finalPosition)
        }
      }
      val viewParent = view.parent
      if (viewParent != null) {
        (viewParent as ViewGroup).removeView(view)
      }
      container.addView(view)
      return view
    }

    override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {}
    override fun isViewFromObject(view: View, `object`: Any): Boolean {
      return view === `object`
    }

    override fun getItemPosition(`object`: Any): Int {
      return POSITION_NONE
    }
  }

  private class AutoPlayTask(banner: Banner) : Runnable {
    private val mBanner: WeakReference<Banner>

    init {
      mBanner = WeakReference(banner)
    }

    override fun run() {
      val banner = mBanner.get()
      if (banner != null) {
        banner.startAutoPlay()
        banner.switchToNextPage()
      }
    }
  }

  /**
   *
   * @param <V> item 视图类型，如果没有在 setData 方法里指定自定义的 item 布局资源文件的话，这里的 V 就是 ImageView
   * @param <M> item 数据模型
  </M></V> */
  interface Delegate {
    fun onBannerItemClick(banner: Banner?, itemView: View, model: Any?, position: Int)
  }

  /**
   * 适配器，在 fillBannerItem 方法中填充数据，加载网络图片等
   *
   * @param <V> item 视图类型，如果没有在 setData 方法里指定自定义的 item 布局资源文件的话，这里的 V 就是 ImageView
   * @param <M> item 数据模型
  </M></V> */
  interface Adapter {
    fun fillBannerItem(banner: Banner?, itemView: View, model: Any?, position: Int)
  }

  /**
   * 引导页「进入」和「跳过」按钮点击事件监听器，
   */
  interface GuideDelegate {
    fun onClickEnterOrSkip()
  }

  companion object {
    private const val RMP = LayoutParams.MATCH_PARENT
    private const val RWC = LayoutParams.WRAP_CONTENT
    private const val LWC = LinearLayout.LayoutParams.WRAP_CONTENT
    private const val NO_PLACEHOLDER_DRAWABLE = -1
    private const val VEL_THRESHOLD = 400
    private val sScaleTypeArray = arrayOf(
      ScaleType.MATRIX,
      ScaleType.FIT_XY,
      ScaleType.FIT_START,
      ScaleType.FIT_CENTER,
      ScaleType.FIT_END,
      ScaleType.CENTER,
      ScaleType.CENTER_CROP,
      ScaleType.CENTER_INSIDE
    )
  }
}